#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include "phypos.h"
#include "soffset.h"
#include "logger.h"

#define DEFAULT_REFPOS_SCALE (1)
#define DEFAULT_SOFFSET_BYTES (8)
#define SOFFSET_MAX_FORMATSTRLEN (32 + 2 + 10)


inline
mrr_byte __soffset_byte_length(mrr_soffset *sof) {
	
	return *sof +1;
}

mrr_soffset *mrr_soffset_new() {

	mrr_byte *sofp;
	
	mrr_mem_alloc(&sofp, DEFAULT_SOFFSET_BYTES + 1);
	memset(sofp, 0, DEFAULT_SOFFSET_BYTES + 1);
	*sofp = DEFAULT_SOFFSET_BYTES;

	return sofp;
}

mrr_soffset * mrr_refpos_clone(mrr_soffset *ptr) {

	register mrr_byte bytenum;

	bytenum = __soffset_byte_length(ptr);

	mrr_soffset *newptr;

	mrr_mem_alloc(&newptr, bytenum);
	memcpy(newptr, ptr, bytenum);

	return newptr;
}

void mrr_soffset_free(mrr_soffset * p) {

	mrr_mem_free(p);
}

/**
 * Compute a = a + b, the a and b are two scaled offsets.
 */
int mrr_soffset_plus(mrr_soffset **a_inout, mrr_soffset *b) {
	
	mrr_soffset *heada, *headb;

	heada = *a_inout;
	headb = b;
	
	if (*heada != 8 || *headb != 8) {
		mrr_fatal("NO implementation!");
	}
	
	heada++;
	headb++;
	
	*((int64_t*)heada) += *((int64_t *)headb);
	
	return MRR_ERRNO_SUCCESS;
}

void mrr_soffset_reverse(mrr_soffset *pos) {
	
	if (*pos != 8) {
		mrr_fatal("NO implementation!");
	}
	
	pos++;
	
	*((int64_t*)pos) = -*((int64_t*)pos);
}

char* __soffset_write_rvect(char* buf, mrr_soffset *sof) {
	
	if (*sof != 8) {
		mrr_fatal("NO implementation!");
	}
	
	sof++;
	
	int64_t offset = *((int64_t*)sof);
	char *str;
	mrr_mem_alloc(&str, 19);
	
	char *p = str;
	*p++ = 'S';
	if (offset < 0) {
		*p++ = '-';
		offset = -offset;
	} else {
		*p++ = '+';
	}
	
	snprintf(p, 18, "%X", offset);
	
	p = str;
	
	while (*p!='\0') {
		*buf++ = *p++;
	}
	*buf = '\0';
	
	mrr_mem_free(str);
}

int mrr_soffset_fwrite(mrr_soffset *sof, FILE* file) {

	int result;
	char* buf;
	result = mrr_mem_alloc(&buf, SOFFSET_MAX_FORMATSTRLEN);
	if (result)
		return result;

	__soffset_write_rvect(buf, sof);

	fwrite(buf, 1, strlen(buf), file);

	mrr_mem_free(buf);
}

inline
int mrr_soffset_write(mrr_soffset *sof) {

	return mrr_soffset_fwrite(sof, stdout);
}

/**
 * %v
 */
int mrr_rvect_format(char **template, ...) {

	va_list args;
	va_start(args, template);

	char *str;
	str = *template;

	// initialize the buffer
	int itemnum = 10;
	char **map;
	char *buf;
	char **map_top;
	char *buf_top;

	mrr_mem_alloc(&map, sizeof(char*) * itemnum);
	// TODO *CRASH RISK* refine memory model here!!! 
	mrr_mem_alloc(&buf, sizeof(char) * itemnum * 32);
	map_top = map;
	buf_top = buf;

	size_t template_len = 0;
	while ( *str != '\0') {

		template_len ++;

		if ( *str++ != '%') {
			continue;
		}

		template_len ++;

		if ( *str++ != 'v') {
			continue;
		}

		*map_top++ = str - 2; // point the map at '%s' . 

		mrr_soffset *pos;
		pos = va_arg( args, mrr_soffset * );
		char *xxx;
		xxx = buf_top;
		buf_top = __soffset_write_rvect(buf_top, pos);

		itemnum--;
		assert(itemnum >=0);
	}

	if (map != map_top) {
		// assemble new string

		template_len += (buf_top - buf) - (10 - itemnum); // new size

		char *newstr;
		mrr_mem_alloc(&newstr, sizeof(char) * template_len);

		char *u;
		char *w;
		char *x;
		u = newstr; // the new allocated string
		w = *template; // the old template
		x = buf;

		char **mapp;
		for (mapp = map; mapp < map_top; mapp ++) {

			char *v; // the old template's index for char '%'

			v = *mapp;
			int len;
			len = v - w; // the fragment's length
			memcpy(u, w, len);
			u += len;
			w += len;
			w += 2; // skip '%v' 

			while (*x != '\0') {
				*u++ = *x++;
			}
			x++;
		}

		while (*w != '\0') {
			*u++ = *w++;
		}
		*u = '\0';

		mrr_mem_free(*template);

		*template = newstr;
	}

	mrr_mem_free(buf);
	mrr_mem_free(map);
	va_end(args);

	return MRR_ERRNO_SUCCESS;
}

int mrr_soffset_step(mrr_soffset **sof, int step) {
	
	mrr_soffset *sofp;
	
	sofp = *sof;
	
	if (*sofp != 8) {
		mrr_fatal("NO implementation!");
	}
	
	sofp++;
	
	int64_t *p64;
	
	p64 = (int64_t*)sofp;
	
	if (*p64 <0) {
		*p64 += -step;
	} else {
		*p64 += step;
	}
}
